---
title: 3.1 关于
description: 一款企业级 Web 服务开发框架
sidebar:
    order: 1
---


import idea_setting from './img/idea_setting.png';
import demo from './img/demo.png';
import {Aside, TabItem, Tabs} from "@astrojs/starlight/components";

Feat Cloud 提供了一种面向企业级应用开发的解决方案，它的定位有点像 SpringBoot。

设计方面，Feat Cloud 尽最大可能保留了 SpringBoot 的使用习惯，以此降低开发者的学习成本。

技术方面，Feat Cloud 在编译期对代码进行静态分析，最大化提升服务运行性能，降低资源消耗。

## 3.1.1 准备工作

### 3.1.1.2 引入 Maven 依赖
创建一个 Maven 项目，在 `pom.xml` 文件中添加以下依赖：
```xml title=pom.xml
<dependency>
    <groupId>tech.smartboot.feat</groupId>
    <artifactId>feat-cloud-starter</artifactId>
    <version>${feat.version}</version>
</dependency>
```
<Aside>
    **$\{feat.version}**
    需根据实际情况设置，建议使用[最新版本](https://central.sonatype.com/artifact/tech.smartboot.feat/feat-cloud-starter)。
</Aside>

### 3.1.1.3 配置 IDEA
调整 IDEA 编译配置，以启用 Feat Cloud 的静态优化功能，**否则请求将无法正常路由**。

设置路径为：`Preferences -> Build, Execution, Deployment -> Build Tools -> Maven -> Runner`，
勾选`Delegate IDE build/run actions to Maven` 并点击 `OK` 保存配置。

<img src={idea_setting.src} alt="hello world" width="60%" className="shadow"/>

## 3.1.2 快速启动
在 Maven 工程中创建一个 `FeatCloudDemo.java` 文件，添加以下代码：
``` java title=FeatCloudDemo.java
@Controller
public class FeatCloudDemo {
    @RequestMapping("/cloud")
    public String helloWorld() {
        return "hello Feat Cloud";
    }

    public static void main(String[] args) {
        Feat.cloudServer().listen();
    }
}
```
启动程序，打开浏览器访问 `http://localhost:8080/cloud`。
<img src={demo.src} alt="hello world" width="60%" className="shadow"/>


## 3.1.3 工作原理
从上面的代码可以看出，Feat Cloud 是基于注解的方式进行服务开发的。
通常来说，框架会在运行时解析注解并生成对应的服务。

但是，Feat Cloud 却是在编译时对代码进行静态分析，生成对应的服务。
其核心原理是**运用了 APT（Annotation Processing Tool）技术，并结合 ServiceLoader 实现了 0 反射的服务加载机制**。


### 3.1.3.1 静态转码
以 FeatCloudDemo 为例，开发人员编写的源代码在编译时经过一次静态转码，生成了一个新的 java 文件 `FeatCloudDemoBeanAptLoader.java`。
<Tabs>
    <TabItem label="源代码">
        ``` java title=FeatCloudDemo.java
        @Controller
        public class FeatCloudDemo {
            @RequestMapping("/cloud")
            public String helloWorld() {
                return "hello Feat Cloud";
            }

            public static void main(String[] args) {
                Feat.cloudServer().listen();
            }
        }
        ```
    </TabItem>
    <TabItem label="静态转码">
        ```java title=FeatCloudDemoBeanAptLoader.java
        public class FeatCloudDemoBeanAptLoader extends AbstractServiceLoader {
            private FeatCloudDemo bean;

            public void loadBean(ApplicationContext applicationContext) throws Throwable {
                bean = new FeatCloudDemo();
                applicationContext.addBean("featCloudDemo", bean);
            }

            public void autowired(ApplicationContext applicationContext) {

            }

            public void router(Router router) {
                router.route("/cloud", req -> {
                    String rst = bean.helloWorld();
                    byte[] bytes = rst.getBytes("UTF-8");
                    req.getResponse().setContentLength(bytes.length);
                    req.getResponse().write(bytes);
                });
            }

            public void destroy() throws Throwable {
            }

            public void postConstruct(ApplicationContext applicationContext) throws Throwable {}
            }
        ```
    </TabItem>

</Tabs>

可以从静态转码后的代码中看出，对于 bean 的实例化，是通过 `new` 关键字进行的，而不是通过反射。

对于路由的配置，也是通过调用 Feat Server 中的 Router 方法进行的，也不是通过反射。

因此，**Feat Cloud 可以在提供优雅的开发体验的同时，也极大地保留了 Feat Server 框架的性能优势。**

### 3.1.3.2 服务载入
Controller 在编译时完成转码后，下一步便是需要在程序启动后能够被正确加载，此处需要用到的技术便是 `java.util.ServiceLoader`。

Controller 静态转码所生成的类文件默认实现了 `tech.smartboot.feat.cloud.service.CloudService` 接口，同时会自动生成一个 service 文件：

**`META-INF/services/tech.smartboot.feat.cloud.service.CloudService`**

当调用 ApplicationContext@start 方法时：
1. 首先通过 **ServiceLoader.load(CloudService.class)** 加载所有实现了 **CloudService** 接口的类。并根据 `isIgnore` 规则过滤出有效的服务。
2. 遍历所有服务，调用其 `loadBean` 方法，完成 bean 的实例化。
3. 遍历所有服务，调用其 `autowired` 方法，完成各实例的依赖注入。
4. 遍历所有服务，调用其 `postConstruct` 方法，完成各实例的初始化。
5. 遍历所有服务，调用其 `router` 方法，完成 Controller 路由的配置。

<Tabs>
    <TabItem label="ApplicationContext.java">

        ```java title=ApplicationContext.java "ServiceLoader.load" "isIgnore(service)" "service.loadBean" "service.autowired" "service.postConstruct" "service.router"
        public class ApplicationContext {

            ...

            public void start() throws Throwable {
                for (CloudService service : ServiceLoader.load(CloudService.class)) {
                    if (isIgnore(service)) {
                        continue;
                    }
                    services.add(service);
                }
                for (CloudService service : services) {
                    service.loadBean(this);
                }

                for (CloudService service : services) {
                    service.autowired(this);
                }
                for (CloudService service : services) {
                    service.postConstruct(this);
                }
                for (CloudService service : services) {
                    service.router(router);
                }
            }

            ...
        }
        ```
    </TabItem>
    <TabItem label="CloudService.java">
        ```java title=CloudService.java
        public interface CloudService {
            void loadBean(ApplicationContext context) throws Throwable;

            void autowired(ApplicationContext context);

            void postConstruct(ApplicationContext context) throws Throwable;

            void destroy() throws Throwable;

            void router(Router router);
        }
        ```
    </TabItem>
</Tabs>

### 3.1.3.3 启动服务
在这个步骤中，由于 `ApplicationContext@start` 中已经完成了所有服务的实例化、依赖注入、初始化、路由配置等工作，因此，最后一步便是将 Router 实例设置到 HttpServer 中，启动服务。

```java title=Feat.java "application.start();" "application.destroy();" "application::destroy" "application.getRouter()"
public static HttpServer cloudServer(Consumer<CloudOptions> options) {
    CloudOptions opt = new CloudOptions();
    options.accept(opt);
    opt.serverName("feat-cloud");
    ApplicationContext application = new ApplicationContext(opt);
    opt.getExternalBeans().forEach(application::addBean);
    application.start();

    HttpServer server = Feat.httpServer(opt);
    ...
    server.httpHandler(application.getRouter());
    return server;
}
```
